#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
#include <omp.h>
#include "util.h"

#define NUM_PAL 2921103
#define TAM_PAL 15
#define NUM_TH 3

int main (int argc, char ** argv){
	
	double tempo, inicial = omp_get_wtime();
	int rank;
	int r1=0,r2=0;
	int chamadaPalim = 0, chamadaCrivo = 0; 

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	
	if(rank == 0){
		FILE *arq;
		char **texto;
		int i, j, flag;
		char end[2];
		end[0] = -1;
		end[1] = '\0';

		/* 
    		Faz alocação necessária de memória, levando em conta os dados colhidos
     		em análise feita pela equipe anteriormente a execução, portanto é
     		importante os #define's NUM_TH, NUM_PAL E TAM_PAL estarem preenchidos
     		corretamente 
  		*/
		texto = (char **)malloc(NUM_TH*NUM_PAL*sizeof(char *));
		for(j = 0; j < NUM_TH*NUM_PAL; j++)
			texto[j] = (char *)malloc(TAM_PAL*sizeof(char));
		

		arq = fopen("wikipedia.txt", "r");
		/*
    		Faz a leitura do arquivo e vai preenchendo a estrutura texto, cada thread,
    		ou seja, cada texto[i] recebe uma string j sequencialmente.
  		*/
		for(j = 0; j < NUM_TH*NUM_PAL; j++){
			fscanf(arq,"%s", texto[j]);
			if(feof(arq))
				break;
		}

		/*
			Caso o vetor não esteja completamente preenchido, coloca-se um NULL no ponteiro
			posterior a última posição com uma palavra
		*/
		for(; j < NUM_TH*NUM_PAL; j++)
			texto[j] = NULL;

		fclose(arq);
		
		/*
			Vai distribuindo as palavras pelas threads
		*/
		i = 1;	
		for(j = 0; j < NUM_TH*NUM_PAL; j++){
			if(i == NUM_TH+1)
				i = 1;
			/*
				Caso a palavra seja válida, ela é enviada para uma thread, caso contrário
				um sinal para não processar é enviado.
			*/			
			if(texto[j] != NULL) {
				MPI_Bsend(texto[j], TAM_PAL, MPI_CHAR, i, 0, MPI_COMM_WORLD);
			} else {
				MPI_Bsend(end, 2, MPI_CHAR, i, 0, MPI_COMM_WORLD);
			}
			i++;
		}
		/* Fica esperando a resposta das threads sobre a contagem de palíndromos e
			de números primos
		*/
		MPI_Recv(&i, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&j, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		r1 += i;	
		r2 += j;
		MPI_Recv(&i, 1, MPI_INT, 2, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&j, 1, MPI_INT, 2, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		r1 += i;	
		r2 += j;
		MPI_Recv(&i, 1, MPI_INT, 3, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&j, 1, MPI_INT, 3, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		r1 += i;	
		r2 += j;

		tempo = omp_get_wtime() - inicial;

		printf("Numero de palindromos: %d\n", r1);
		printf("Numero de primos: %d\n", r2);
		printf("Tempo de execução: %.2lf seg\n", tempo);
	}
	/*
		Como o código das outras três threads é o mesmo, apenas um else aqui basta
	*/
	else {
		int i=0, flag, tam, asc;
		int numPalim, primo;
		char palavra[TAM_PAL];
		double tempoPal=0.0, tempoPalIni=0.0, tempoCrivo=0.0, tempoCrivoIni=0.0;

		numPalim = 0;
		primo = 0;
		do {
			MPI_Recv(palavra, TAM_PAL, MPI_CHAR, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		
			/* 
				Se não for um sinal de ignorar processa a palavra
			*/	
			if( palavra[0] != -1) {			
				tam = strlen(palavra);
				
				chamadaPalim++;
				tempoPalIni = omp_get_wtime();
				asc = palim(palavra, tam);
				tempoPal = tempoPal + (omp_get_wtime() - tempoPalIni);

				if(asc > 0){
					chamadaCrivo++;
					tempoCrivoIni = omp_get_wtime();						
					if(crivo(asc)) {
						primo++;
					}
					tempoCrivo = tempoCrivo + (omp_get_wtime() - tempoCrivoIni);
					numPalim++;
				}
			}				
			i++;
		} while(i < NUM_PAL);
		printf("Processo %d - Palindromos: [%d , %.2lf] - Primos: [%d , %.2lf]\n",rank,chamadaPalim,tempoPal,chamadaCrivo,tempoCrivo);

		/*
			Envia os resultados para a thread mestra
		*/				
		MPI_Send(&numPalim, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
		MPI_Send(&primo, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
		
	}

	MPI_Finalize();



return 0;
}

